From 3e616152638b42fb283da2fc3e91c824fac77c46 Mon Sep 17 00:00:00 2001 From: "kaf24@firebug.cl.cam.ac.uk" Date: Tue, 13 Dec 2005 19:20:29 +0100 Subject: [PATCH] Fix unmask_evtchn() when the port is bound to a different VCPU. Signed-off-by: Keir Fraser --- linux-2.6-xen-sparse/arch/xen/kernel/evtchn.c | 38 +++++++++++++++++ linux-2.6-xen-sparse/include/asm-xen/evtchn.h | 42 ++++--------------- xen/common/event_channel.c | 41 +++++++++++++++++- xen/include/public/event_channel.h | 11 +++++ xen/include/xen/event.h | 2 +- 5 files changed, 99 insertions(+), 35 deletions(-) diff --git a/linux-2.6-xen-sparse/arch/xen/kernel/evtchn.c b/linux-2.6-xen-sparse/arch/xen/kernel/evtchn.c index 7ebe3f5e58..b57116d3ea 100644 --- a/linux-2.6-xen-sparse/arch/xen/kernel/evtchn.c +++ b/linux-2.6-xen-sparse/arch/xen/kernel/evtchn.c @@ -639,6 +639,44 @@ void notify_remote_via_irq(int irq) } EXPORT_SYMBOL(notify_remote_via_irq); +void mask_evtchn(int port) +{ + shared_info_t *s = HYPERVISOR_shared_info; + synch_set_bit(port, &s->evtchn_mask[0]); +} +EXPORT_SYMBOL(mask_evtchn); + +void unmask_evtchn(int port) +{ + shared_info_t *s = HYPERVISOR_shared_info; + unsigned int cpu = smp_processor_id(); + vcpu_info_t *vcpu_info = &s->vcpu_info[cpu]; + + /* Slow path (hypercall) if this is a non-local port. */ + if (unlikely(cpu != cpu_from_evtchn(port))) { + evtchn_op_t op = { .cmd = EVTCHNOP_unmask, + .u.unmask.port = port }; + (void)HYPERVISOR_event_channel_op(&op); + return; + } + + synch_clear_bit(port, &s->evtchn_mask[0]); + + /* + * The following is basically the equivalent of 'hw_resend_irq'. Just + * like a real IO-APIC we 'lose the interrupt edge' if the channel is + * masked. + */ + if (synch_test_bit(port, &s->evtchn_pending[0]) && + !synch_test_and_set_bit(port / BITS_PER_LONG, + &vcpu_info->evtchn_pending_sel)) { + vcpu_info->evtchn_upcall_pending = 1; + if (!vcpu_info->evtchn_upcall_mask) + force_evtchn_callback(); + } +} +EXPORT_SYMBOL(unmask_evtchn); + void irq_resume(void) { evtchn_op_t op; diff --git a/linux-2.6-xen-sparse/include/asm-xen/evtchn.h b/linux-2.6-xen-sparse/include/asm-xen/evtchn.h index 788e8d7b5e..8c573b13e0 100644 --- a/linux-2.6-xen-sparse/include/asm-xen/evtchn.h +++ b/linux-2.6-xen-sparse/include/asm-xen/evtchn.h @@ -79,46 +79,16 @@ extern int bind_ipi_to_irqhandler( */ extern void unbind_from_irqhandler(unsigned int irq, void *dev_id); -/* - * Unlike notify_remote_via_evtchn(), this is safe to use across - * save/restore. Notifications on a broken connection are silently dropped. - */ -void notify_remote_via_irq(int irq); - extern void irq_resume(void); /* Entry point for notifications into Linux subsystems. */ asmlinkage void evtchn_do_upcall(struct pt_regs *regs); /* Entry point for notifications into the userland character device. */ -void evtchn_device_upcall(int port); +extern void evtchn_device_upcall(int port); -static inline void mask_evtchn(int port) -{ - shared_info_t *s = HYPERVISOR_shared_info; - synch_set_bit(port, &s->evtchn_mask[0]); -} - -static inline void unmask_evtchn(int port) -{ - shared_info_t *s = HYPERVISOR_shared_info; - vcpu_info_t *vcpu_info = &s->vcpu_info[smp_processor_id()]; - - synch_clear_bit(port, &s->evtchn_mask[0]); - - /* - * The following is basically the equivalent of 'hw_resend_irq'. Just - * like a real IO-APIC we 'lose the interrupt edge' if the channel is - * masked. - */ - if (synch_test_bit(port, &s->evtchn_pending[0]) && - !synch_test_and_set_bit(port / BITS_PER_LONG, - &vcpu_info->evtchn_pending_sel)) { - vcpu_info->evtchn_upcall_pending = 1; - if (!vcpu_info->evtchn_upcall_mask) - force_evtchn_callback(); - } -} +extern void mask_evtchn(int port); +extern void unmask_evtchn(int port); static inline void clear_evtchn(int port) { @@ -134,6 +104,12 @@ static inline void notify_remote_via_evtchn(int port) (void)HYPERVISOR_event_channel_op(&op); } +/* + * Unlike notify_remote_via_evtchn(), this is safe to use across + * save/restore. Notifications on a broken connection are silently dropped. + */ +extern void notify_remote_via_irq(int irq); + #endif /* __ASM_EVTCHN_H__ */ /* diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c index c3ef30ce34..eeccc0e160 100644 --- a/xen/common/event_channel.c +++ b/xen/common/event_channel.c @@ -3,7 +3,7 @@ * * Event notifications from VIRQs, PIRQs, and other domains. * - * Copyright (c) 2003-2004, K A Fraser. + * Copyright (c) 2003-2005, K A Fraser. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -541,6 +541,41 @@ static long evtchn_bind_vcpu(evtchn_bind_vcpu_t *bind) return rc; } +static long evtchn_unmask(evtchn_unmask_t *unmask) +{ + struct domain *d = current->domain; + shared_info_t *s = d->shared_info; + int port = unmask->port; + struct vcpu *v; + + spin_lock(&d->evtchn_lock); + + if ( unlikely(!port_is_valid(d, port)) ) + { + spin_unlock(&d->evtchn_lock); + return -EINVAL; + } + + v = d->vcpu[evtchn_from_port(d, port)->notify_vcpu_id]; + + /* + * These operations must happen in strict order. Based on + * include/xen/event.h:evtchn_set_pending(). + */ + if ( test_and_clear_bit(port, &s->evtchn_mask[0]) && + test_bit (port, &s->evtchn_pending[0]) && + !test_and_set_bit (port / BITS_PER_LONG, + &v->vcpu_info->evtchn_pending_sel) && + !test_and_set_bit (0, &v->vcpu_info->evtchn_upcall_pending) ) + { + evtchn_notify(v); + } + + spin_unlock(&d->evtchn_lock); + + return 0; +} + long do_event_channel_op(evtchn_op_t *uop) { long rc; @@ -602,6 +637,10 @@ long do_event_channel_op(evtchn_op_t *uop) rc = evtchn_bind_vcpu(&op.u.bind_vcpu); break; + case EVTCHNOP_unmask: + rc = evtchn_unmask(&op.u.unmask); + break; + default: rc = -ENOSYS; break; diff --git a/xen/include/public/event_channel.h b/xen/include/public/event_channel.h index bb15a87b7d..03d2039cd3 100644 --- a/xen/include/public/event_channel.h +++ b/xen/include/public/event_channel.h @@ -164,6 +164,16 @@ typedef struct evtchn_bind_vcpu { uint32_t vcpu; } evtchn_bind_vcpu_t; +/* + * EVTCHNOP_unmask: Unmask the specified local event-channel port and deliver + * a notification to the appropriate VCPU if an event is pending. + */ +#define EVTCHNOP_unmask 9 +typedef struct evtchn_unmask { + /* IN parameters. */ + evtchn_port_t port; +} evtchn_unmask_t; + typedef struct evtchn_op { uint32_t cmd; /* EVTCHNOP_* */ union { @@ -176,6 +186,7 @@ typedef struct evtchn_op { evtchn_send_t send; evtchn_status_t status; evtchn_bind_vcpu_t bind_vcpu; + evtchn_unmask_t unmask; } u; } evtchn_op_t; diff --git a/xen/include/xen/event.h b/xen/include/xen/event.h index a8632eaf09..b3c7713679 100644 --- a/xen/include/xen/event.h +++ b/xen/include/xen/event.h @@ -3,7 +3,7 @@ * * A nice interface for passing asynchronous events to guest OSes. * - * Copyright (c) 2002, K A Fraser + * Copyright (c) 2002-2005, K A Fraser */ #ifndef __XEN_EVENT_H__ -- 2.30.2